<?php

use EchoPHP\Foundation\Container;
use EchoPHP\Http\Exception\HttpResponseException;
use EchoPHP\Http\Response;
use EchoPHP\Http\ResponseFactory;
use Dotenv\Environment\DotenvFactory;
use Dotenv\Environment\Adapter\PutenvAdapter;
use Dotenv\Environment\Adapter\EnvConstAdapter;
use Dotenv\Environment\Adapter\ServerConstAdapter;
use EchoPHP\Support\HigherOrderTapProxy;

if (!function_exists('app')) {
    /**
     * 获取应用程序实例中绑定的实例对象
     * @param null $abstract
     * @param array $parameters
     * @return Container|mixed|object
     * @throws Exception
     */
    function app($abstract = null, array $parameters = []) {
        if (is_null($abstract)) {
            return Container::getInstance();
        }
        return Container::getInstance()->make($abstract, $parameters);
    }
}

if (!function_exists('config')) {
    /**
     * 获取应用程序配置内容
     * @param null $key
     * @param null $default
     * @return Container|mixed|object
     * @throws Exception
     */
    function config($key = null, $default = null) {
        if (is_null($key)) {
            return app('config');
        }
        return app('config')->get($key, $default);
    }
}

if (!function_exists('env')) {
    /**
     * 从环境变量中获取一个值
     * @param $key
     * @param null $default
     * @return bool|mixed|string|null
     */
    function env($key, $default = null) {
        static $dotEnvVariables;

        if (is_null($dotEnvVariables)) {
            $dotEnvVariables = (new DotenvFactory([new EnvConstAdapter(), new PutenvAdapter(), new ServerConstAdapter()]))->createImmutable();
        }

        $value = $dotEnvVariables->get($key);
        switch (strtolower($value)) {
            case 'true':
            case '(true)':
                $value = true;
                break;
            case 'false':
            case '(false)':
                $value = false;
                break;
            case 'empty':
            case '(empty)':
                $value = '';
                break;
            case 'null':
            case '(null)':
                $value = null;
                break;
        }
        //清除value中得引号  如 ABC="'abc'"  不过滤获取的就是 'abc'  实际需要 abc
        if (preg_match('/\A([\'"])(.*)\1\z/', $value, $matches)) {
            $value = $matches[2];
        }
        return is_null($value) ? $default : $value;

    }
}

if (!function_exists('response')) {
    /**
     * 响应工厂
     * @param string $content
     * @param int $status
     * @param array $headers
     * @return \EchoPHP\Http\Response|ResponseFactory
     * @throws Exception
     */
    function response($content = '', $status = 200, array $headers = []) {
        /**
         * @var $factory ResponseFactory
         */
        $factory = app(ResponseFactory::class);

        if (func_num_args() === 0) {
            return $factory;
        }

        return $factory->make($content, $status, $headers);
    }
}

if (!function_exists('view')) {
    /**
     * 如果模板名称为空，则返回引擎实例， 否则快速创建模板并渲染
     * @param string|null $name
     * @param array $data
     * @return mixed
     * @throws Exception
     */
    function view($name = null, array $data = array()) {
        if (is_null($name)) {
            return app('view');
        }
        return app('view')->render($name, $data);
    }
}

if (!function_exists('session')) {
    /**
     * session set / get 方法， key为null则返回session操作类， key为数组 执行set方法， 否则执行get方法
     * @param string|null|array $key
     * @param null $default
     * @return Container|mixed|object
     * @throws Exception
     */
    function session($key = null, $default = null) {
        if (is_null($key)) {
            return app('session');
        }
        if (is_array($key)) {
            return app('session')->set($key);
        }

        return app('session')->get($key, $default);
    }
}

if (!function_exists('encrypt')) {
    /**
     * 可逆加密方法
     * @param string $data 需要加密的明文
     * @param string $key 加密密钥
     * @return string   密文
     * @throws Exception
     */
    function encrypt($data, $key = '') {
        if (empty($key)) {
            $key = env('ENCRYPT_KEY', 'encrypt');
        }
        $key = md5($key);
        $x = 0;
        $len = strlen($data);
        $l = strlen($key);
        $char = '';
        for ($i = 0; $i < $len; $i++) {
            if ($x == $l) {
                $x = 0;
            }
            $char .= $key{$x};
            $x++;
        }
        $str = '';
        for ($i = 0; $i < $len; $i++) {
            $str .= chr(ord($data{$i}) + (ord($char{$i})) % 256);
        }
        return base64_encode($str);
    }
}

if (!function_exists('decrypt')) {
    /**
     * 可逆解密方法
     * @param string $data 需要解密的密文
     * @param string $key 加密密钥
     * @return string   明文
     * @throws Exception
     */
    function decrypt($data, $key = '') {
        if (empty($key)) {
            $key = env('ENCRYPT_KEY', 'encrypt');
        }
        $key = md5($key);
        $x = 0;
        $data = base64_decode($data);
        $len = strlen($data);
        $l = strlen($key);
        $char = '';
        for ($i = 0; $i < $len; $i++) {
            if ($x == $l) {
                $x = 0;
            }
            $char .= substr($key, $x, 1);
            $x++;
        }
        $str = '';
        for ($i = 0; $i < $len; $i++) {
            if (ord(substr($data, $i, 1)) < ord(substr($char, $i, 1))) {
                $str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1)));
            } else {
                $str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1)));
            }
        }
        return $str;
    }
}

if (!function_exists('halt')) {
    /**
     * 输出变量并中断脚本
     * @param $expression
     */
    function halt(...$expression) {
        var_dump(...$expression);
        throw new HttpResponseException(new Response());
    }
}

if (!function_exists('e_dump')) {
    /**
     * 返回给定异常的可视化记录，用于写入日志
     * @param Exception $exception
     * @param bool $traceback 为true时，输出traceback，否则只输出抛出异常位置。
     * @param bool $verbose 为false时，traceback只截取应用层；否则输出完整traceback。
     * @return string 可视化异常说明文本
     */
    function e_dump(Exception $exception, $traceback = true, $verbose = false) {
        if ($traceback) {
            $traceText = $exception->getTraceAsString();
            if (!$verbose) {
                $trace = $exception->getTrace();
                $hasUserCode = false;
                foreach ($trace as $i => $step) {
                    $isUserCode = empty($step['file']) || strpos($step['file'], ROOT_PATH . '/framework/src/EchoPHP/') !== 0;
                    $hasUserCode |= $isUserCode;
                    if ($hasUserCode && !$isUserCode) break;
                }
                $traceText = implode("\r\n", array_slice(explode(PHP_EOL, $traceText), 0, $i));
            }
            $description = " traceback:\r\n" . $traceText;
        } else {
            $description = ' in file "' . $exception->getFile() . '", line ' . $exception->getLine();
        }
        return '[' . $exception->getMessage() . ']' . $description;
    }
}

if (!function_exists('print_img')) {
    /**
     * 将二进制图像内容可视化打印于html浏览器中，方便调试
     * @param string|array $img 图像完整文件路径，或原始二进制/base64数据（支持数组）
     * @param bool $return 为true时不直接在输出流打印，仅作为返回值。
     * @return string|array 包含指定图像的html标签，若非有效图像则返回$img原始值
     */
    function print_img($img, $return = false) {
        if (is_array($img)) {
            array_walk_recursive($img, function (&$item) {
                if (is_string($item)) $item = str2imgTag($item);
            });
            if (!$return) print_r($img);
        } else {
            if (is_string($img)) $img = str2imgTag($img);
            if (!$return) echo $img;
        }
        return $img;
    }
}

if (!function_exists('str2imgTag')) {
    /**
     * 将单个二进制图像转化为html<img>标签
     * @param string $imgStr 图像完整文件路径，或原始二进制/base64数据
     * @return string 包含指定图像的html标签，若非有效图像则返回$imgStr原始值
     */
    function str2imgTag(string $imgStr) {
        try {
            if (@file_exists($imgStr)) {
                if (@is_dir($imgStr)) return $imgStr;
                if (empty($imgData = file_get_contents($imgStr))) return $imgStr;
            } else {
                $imgData =& $imgStr;
                if ($decoded = @base64_decode($imgData, true)) $imgData =& $decoded;
            }
            $mime = getimagesizefromstring($imgData)['mime'];
            return empty($mime) ? $imgStr : '<img src="data:' . $mime . ';base64,' . base64_encode($imgData) . '">';
        } catch (\Exception $e) {
            return $imgStr;
        }
    }
}

if (!function_exists('formatToLower')) {
    /**
     * 只保留中英文数字，并转换为小写。
     * @param string $str 要转换的文字
     * @return string 转换后的文字
     */
    function formatToLower(string $str) {
        return strtolower(preg_replace('/[^\x{4e00}-\x{9fa5}a-zA-Z0-9]+/u', '', $str));
    }
}

if (!function_exists('is_iterable')) {
    /**
     * 判断变量是否可迭代（作为foreach的合法对象）
     * @param mixed $var 需判断的变量
     * @return bool
     */
    function is_iterable($var) {
        return is_array($var) || (is_object($var) && ($var instanceof Traversable));
    }
}

if ((!function_exists('utf8_str_replace')) &&
    (function_exists('preg_replace')) && (function_exists('preg_quote') && (function_exists('mb_strlen')))) {
    /**
     * UTF-8编码多字节字符串的str_replace函数
     * @param string|array $search 查找文本（可以为数组）
     * @param string|array $replace 替换文本（若为数组，则与$search构成映射关系）
     * @param string|array $subject 替换前的原始内容
     * @return string|array 替换后的结果
     */
    function utf8_str_replace($search, $replace, $subject) {
        if (is_array($subject)) {
            $ret = [];
            foreach ($subject as $key => $val) {
                $ret[$key] = utf8_str_replace($search, $replace, $val);
            }
            return $ret;
        }
        $batchSearchChar = [];
        $batchSearchStr = [];
        foreach ((array)$search as $key => $s) {
            if ($s == '' && $s !== 0) continue;
            $r = is_array($replace) ? ($replace[$key] ?? '') : $replace;
            if ($r == '' && $r !== 0) {
                if (mb_strlen($s, 'UTF-8') > 1) {
                    $batchSearchStr[] = $s;
                } else {
                    $batchSearchChar[] = $s;
                }
            } else {
                $subject = preg_replace('/' . preg_quote($s, '/') . '/u', $r, $subject);
            }
        }
        foreach ($batchSearchStr as &$s) {
            $s = preg_quote($s, '/');
        }
        if (count($batchSearchChar)) {
            $batchSearchStr[] = '[' . preg_quote(implode($batchSearchChar), '/') . ']+';
        }
        if (count($batchSearchStr)) {
            $batchSearch = implode('|', $batchSearchStr);
            if (count($batchSearchStr) > 1 || !count($batchSearchChar)) {
                $batchSearch = '(?:' . $batchSearch . ')+';
            }
            $subject = preg_replace('/' . $batchSearch . '/u', '', $subject);
        }
        return $subject;
    }
}

if (!function_exists('tap')) {
    /**
     * 接受参数并使用该参数执行一个匿名函数，最后将参数返回
     * Call the given Closure with the given value then return the value.
     *
     * @param mixed $value
     * @param callable|null $callback
     * @return mixed
     */
    function tap($value, $callback = null) {
        if (is_null($callback)) {
            return new HigherOrderTapProxy($value);
        }

        $callback($value);

        return $value;
    }
}

if (!function_exists('mergeArrSql')) {
    /**
     * 拼装数组形式的绑定变量
     * @param array $params
     * @return false|string
     */
    function mergeArrSql(array $params) {
        return substr(str_repeat('?,', count($params)), 0, -1);
    }
}

if (!function_exists('multiSort')) {
    /**
     * 二维数组排序
     * @param $data     待排序数组
     * @param $sortCriteria     排序规则   字段名 => 升序或者降序
     * @return bool|mixed
     * @example multiSort($data, ['volume' => SORT_DESC, 'edition' => SORT_ASC])
     */
    function multiSort($data, $sortCriteria) {
        if (!is_array($data) || !is_array($sortCriteria))
            return false;

        $colLists = array();
        foreach ($data as $key => $row) {
            foreach ($sortCriteria as $sortField => $sortCriterion) {
                $colLists[$sortField][$key] = $row[$sortField] ?? [];
            }
        }

        $args = array();
        foreach ($sortCriteria as $sortField => $sortCriterion) {
            if (isset($colLists[$sortField])) {
                $args[] = $colLists[$sortField];
                $args[] = $sortCriterion == SORT_DESC ? SORT_DESC : SORT_ASC;
            }
        }
        if (empty($args)) {
            return $data;
        }
        $args[] = $data;

        array_multisort(...$args);
        return array_pop($args);
    }
}

if (!function_exists('formatIdsToArr')) {
    /**
     * 将多个id组合的字符串格式化为使用逗号分隔的格式，方便后续统一处理
     * @param string $ids
     * @return array
     */
    function formatIdsToArr(string $ids) {
        $ids = preg_replace("/(\r\n)|(\n)|(\s)|(\t)|(\')|(')|(，)/", ',', $ids);
        return array_values(array_unique(array_filter(explode(',', $ids))));
    }
}